<template>
  <div class="app-container">
    <el-button type="primary" @click="handleAddRole">
      {{ $t('permission.addRole') }}
    </el-button>
    <el-button type="danger" @click="handleBatchDeleteRole">
      {{ $t('permission.batchDelete') }}
    </el-button>
    <el-table :data="rolesList" style="width: 100%;margin-top:30px;" border @selection-change="handleSelectionChange">
      <el-table-column
        type="selection"
        align="center"
        width="40"
      />
      <el-table-column
        type="index"
        label="序号"
        align="center"
        width="50"
      />
      <el-table-column align="center" label="角色编号" width="220">
        <template slot-scope="scope">
          {{ scope.row.roleCode }}
        </template>
      </el-table-column>
      <el-table-column align="center" label="角色名称" width="220">
        <template slot-scope="scope">
          {{ scope.row.roleName }}
        </template>
      </el-table-column>
      <el-table-column align="header-center" label="角色业务描述">
        <template slot-scope="scope">
          {{ scope.row.roleDescription }}
        </template>
      </el-table-column>
      <el-table-column align="center" label="操 作">
        <template slot-scope="scope">
          <el-button type="primary" size="small" @click="handleEdit(scope)">
            {{ $t('permission.editPermission') }}
          </el-button>
          <el-button type="danger" size="small" @click="handleDelete(scope)">
            {{ $t('permission.delete') }}
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 编辑角色模态框 -->
    <el-dialog :visible.sync="dialogVisible" :title="dialogType==='edit'?'编辑角色':'新增角色'">
      <el-form :model="role" label-width="100px" label-position="left">
        <el-form-item
          label="角色名称："
        >
          <el-input v-model="role.roleName" placeholder="请填写角色名称" />
        </el-form-item>
        <el-form-item label="角色描述：">
          <el-input
            v-model="role.roleDescription"
            maxlength="200"
            show-word-limit
            :autosize="{ minRows: 2, maxRows: 4}"
            type="textarea"
            placeholder="请填写角色描述内容"
          />
        </el-form-item>
        <!-- 菜单树 -->
        <!-- 属性说明：
  show-checkbox 表示节点是否被选择
  check-strictly：表示在显示复选框的情况下，是否严格的遵循父子不互相关联的做法
    默认为 false  就是相关联的意思 选中父的子菜单时，父也被选中关联起来
    true时，选中父的子菜单时，父菜单不会被选中关联起来
    更多详情参看 https://blog.csdn.net/HH18700418030/article/details/99621030
         -->
        <el-form-item label="路由菜单：">
          <el-tree
            ref="tree"
            :check-strictly="checkStrictly"
            default-expand-all
            :data="routesData"
            :props="defaultProps"
            show-checkbox
            node-key="menukey"
            class="permission-tree"
          />
        </el-form-item>
      </el-form>
      <div style="text-align:right;">
        <el-button type="danger" @click="dialogVisible=false">
          {{ $t('permission.cancel') }}
        </el-button>
        <el-button type="primary" @click="confirmRole">
          {{ $t('permission.confirm') }}
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import path from 'path'
import { deepClone } from '@/utils'
import { getRoutes, getRoutesByRoleCode, getRoles, addRole, deleteRole, updateRole } from '@/api/role'
import i18n from '@/lang'
const { Message } = require('element-ui')

const defaultRole = { // 默认角色对象结构
  roleCode: '', // 角色主键
  roleName: '', // 角色名称
  roleDescription: '', // 角色描述
  validFlag: '1', // 默认有效
  parentCode: '0', // 默增加时，为一级路由菜单
  routes: [] // 角色对应的菜单路由数组列表
}

export default {
  data() {
    return {
      role: Object.assign({}, defaultRole), // 初始化角色对象数据
      routes: [], // 路由菜单数组
      rolesList: [], // 角色数组列表
      currentRoleMenuArray: [], // 当前角色拥有的菜单总数据数组
      rolesSelections: [], // 选中角色数据列
      dialogVisible: false, // 默认模态框隐藏
      dialogType: 'new', // 默认新增模式
      checkStrictly: false,
      defaultProps: {
        children: 'children',
        label: 'title'
      }
    }
  },
  computed: {
    // 处理路由菜单用于路由菜单树的显示
    routesData() {
      return this.routes
    }
  },
  created() {
    // Mock: get all routes（菜单》？？） and roles list from server
    this.getRoutes() // 获取所有路由菜单信息
    this.getRoles() // 获取所有的角色信息
  },
  methods: {
    // 回显数据
    setCheckedNodes(array) {
      this.$refs.tree.setCheckedNodes(array)
    },
    // 请求所有路由菜单
    async getRoutes() {
      const res = await getRoutes()

      this.serviceRoutes = res.data.main // 服务器返回的所有权限菜单对象(菜单名称) 用于展示菜单和角色匹配
      const routes = this.generateRoutes(res.data.main)
      this.routes = this.i18n(this.removechildForRoutes(routes))
      // const size = this.routes.length
      // await this.$store.dispatch('router/creatRoutes', this.routes, size)
    },
    // 请求所有角色
    async getRoles() {
      const res = await getRoles()
      this.rolesList = res.data.main
    },
    i18n(routes) { // 翻译路由
      const app = routes.map(route => {
        route.title = i18n.t(`${route.title}`) // <----
        if (route.children) {
          route.children = this.i18n(route.children)
        }
        return route
      })
      return app
    },
    // Reshape the routes structure so that it looks the same as the sidebar
    // 重塑routes结构，使其看起来与侧边栏相同 用于路由tree的展示
    generateRoutes(routes, basePath = '/') {
      const res = []
      for (let route of routes) { // 遍历 routes 菜单数组对象
        // skip some route 跳过一些route路由
        // 若权限对象属性的hidden字段为true或者1 直接跳过
        if (route.hidden === '1') { continue } // 当route的hidden属性设置为true时，跳过
        // 把字段children 与 菜单对象进行二次处理
        const onlyOneShowingChild = this.onlyOneShowingChild(route.children, route)
        if (route.children && onlyOneShowingChild && !route.alwaysShow) {
          route = onlyOneShowingChild
        }
        const data = {
          path: path.resolve(basePath, route.url),
          title: route.title, // 菜单名称 备注等意思
          menukey: route.menuCode
        }
        // recursive child routes 递归子路由
        if (route.parentCode === '0') { // 判断是否是主菜单 若是则遍历寻找其子菜单
          const RouteBro = this.serviceRoutes.filter(menu => // 与全部的菜单路由对象中，寻找过滤其父菜单编号得到父路由菜单列表
            menu.parentCode === route.menuCode
          )
          // data.children = this.generateRoutes(route.children, data.path)
          data.children = this.generateRoutes(RouteBro, data.path)
        } else {
          data.children = []
        }
        res.push(data)
      }
      return res // 返回总路由菜单
    },
    generateArr(routes) {
      let data = []
      routes.forEach(route => { // 当前的路由菜单对象
        data.push(route)
        if (route.children) {
          const temp = this.generateArr(route.children)
          if (temp.length > 0) {
            data = [...data, ...temp]
          }
        }
      })
      return data
    },
    dropSuccess() {
      this.getRoles()
    },
    modifySuccess() {
      this.getRoles()
    },
    handleSelectionChange(selections) {
      this.rolesSelections = selections
    },
    handleAddRole() {
      this.role = Object.assign({}, defaultRole)
      if (this.$refs.tree) {
        this.$refs.tree.setCheckedNodes([])
      }
      this.dialogType = 'new' // 新增模式
      this.dialogVisible = true
    },
    async handleEdit(scope) {
      this.dialogType = 'edit' // 编辑模式
      this.dialogVisible = true
      this.checkStrictly = true
      this.role = deepClone(scope.row)
      const res = await getRoutesByRoleCode(scope.row.roleCode)
      this.role.routes = res.data.main.length !== 0 ? res.data.main : []
      this.$nextTick(() => {
        const routes = this.removechildForRoutes(this.role.routes)
        this.currentRoleMenuArray = this.generateArr(routes)
        console.log(this.currentRoleMenuArray)
        this.$refs.tree.setCheckedNodes(this.currentRoleMenuArray)// 回显服务器传来的该角色的节点数据
        // set checked state of a node not affects its father and child nodes
        this.checkStrictly = false
      })
    },
    handleBatchDeleteRole() {
      if (this.rolesSelections.length === 0) {
        Message({
          message: '至少选中一个角色进行删除' || 'Error',
          type: 'error',
          duration: 2 * 1000
        })
        return
      }
      this.$confirm('确认删除这么多的角色 ?', '警告', {
        confirmButtonText: '我确认',
        cancelButtonText: '我再想想',
        type: 'warning'
      })
        .then(async() => {
          const res = await deleteRole(this.rolesSelections)
          const resp = res.data.respOutMsgHeader
          if (resp.respCode === 200) {
            Message({
              message: resp.msg || 'Error',
              type: 'error',
              duration: 2 * 1000
            })
            this.dropSuccess() // 重新获取数据
          } else {
            Message({
              message: resp.msg || 'Error',
              type: 'error',
              duration: 2 * 1000
            })
          }
        })
        .catch(err => { console.error(err) })
    },
    handleDelete({ $index, row }) {
      this.$confirm('确认删除该角色 ?', '警告', {
        confirmButtonText: '我确认',
        cancelButtonText: '我再想想',
        type: 'warning'
      })
        .then(async() => {
          await deleteRole([row])
          this.rolesList.splice($index, 1)
          this.dropSuccess() // 重新获取数据
          this.$message({
            type: 'success',
            message: '删除成功!'
          })
        })
        .catch(err => { console.error(err) })
    },
    generateTree(routes, basePath = '/', checkedKeys) {
      const res = []

      for (const route of routes) {
        const routePath = path.resolve(basePath, route.path)
        // recursive child routes
        if (route.children.length >= 1) {
          route.children = this.generateTree(route.children, routePath, checkedKeys)
        }

        if (checkedKeys.includes(routePath) || (route.children && route.children.length >= 1)) {
          res.push(route)
        }
      }
      return res
    },
    removechildForRoutes(routes) {
      const newres = []
      for (const route of routes) {
        if (route.children.length !== 0) {
          newres.push(route)
        }
      }
      return newres
    },
    async confirmRole() {
      // 判断当前模态框的模式
      const isEdit = this.dialogType === 'edit'
      const checkedNodes = this.$refs.tree.getCheckedNodes()
      const fu = this.$refs.tree.getHalfCheckedNodes() // 返回选中子节点的父节点的key
      const resultCheckedNodes = checkedNodes.concat(fu)
      if (isEdit) { // 编辑模式情况下
        if (this.role.roleDescription.length >= 200) {
          this.$message({
            type: 'error',
            message: '修改失败，角色描述长度不能大于200'
          })
          return
        }
        await updateRole(this.role, resultCheckedNodes) // 角色编号和路由菜单数组对象
        for (let index = 0; index < this.rolesList.length; index++) {
          if (this.rolesList[index].roleCode === this.role.roleCode) {
            this.rolesList.splice(index, 1, Object.assign({}, this.role))
            break
          }
        }
        this.modifySuccess() // 重新获取数据
        // window.location.reload()
      } else { // 增加模式情况下
        const { data } = await addRole(this.role, resultCheckedNodes)
        this.role.roleCode = data.roleCode
        this.rolesList.push(this.role)
        this.modifySuccess() // 重新获取数据
      }
      const { roleDescription, roleName } = this.role
      this.dialogVisible = false
      this.$notify({
        title: '操作成功',
        dangerouslyUseHTMLString: true,
        message: `
            <div>角色名称: ${roleName}</div>
            <div>角色描述: ${roleDescription}</div>
          `,
        type: 'success'
      })
    },
    // reference: src/view/layout/components/Sidebar/SidebarItem.vue
    onlyOneShowingChild(children = [], parent) {
      let onlyOneChild = null
      // 过滤 hidden != false的子对象
      const showingChildren = children.filter(item => item.hidden !== '0')

      // When there is only one child route, the child route is displayed by default
      // 如果只有一个子对象， 则默认展示该子对象
      if (showingChildren.length === 1) {
        onlyOneChild = showingChildren[0]
        // path.resolve([path1][, path2][, ...]) 从右向左依次拼接该路径序列，直到构成一个绝对路径。
        // 例如，输入参数：/foo, /bar, baz, 调用函数path.resolve('/foo', '/bar', 'baz')后返回结果是 /bar/baz；
        onlyOneChild.path = path.resolve(parent.path, onlyOneChild.path)
        return onlyOneChild
      }

      // Show parent if there are no child route to display
      if (showingChildren.length === 0) {
        onlyOneChild = { ... parent, path: '', noShowingChildren: true }
        return onlyOneChild
      }

      return false
    }
  }
}
</script>

<style lang="scss" scoped>
.app-container {
  .roles-table {
    margin-top: 30px;
  }
  .permission-tree {
    margin-bottom: 30px;
  }
}
</style>
